package core.xml;

import java.io.File;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Comment;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;


public class SimpleXMLParser {
	private static final String SCHEMA_LANGUAGE_ATTR_NAME = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";

	private static final String SCHEMA_LANGUAGE_ATTR_VALUE = "http://www.w3.org/2001/XMLSchema";

	private static final String SCHEMA_SOURCE_ATTR_NAME = "http://java.sun.com/xml/jaxp/properties/schemaSource";

	/**
	 * An error handler for use when parsing XML documents.
	 */
	private ErrorHandler errorHandler = null;

	/**
	 * An entity resolver for use when parsing XML documents.
	 */
	private EntityResolver entityResolver = null;

	/**
	 * A flag for validate XML documents
	 */
	private boolean validating = false;

	private boolean useSchema = false;

	/**
	 * SchemaԴ
	 */
	private String schemaSource = null;

	private DocumentBuilder getDocumentBuilder() throws ParserConfigurationException {
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

		factory.setNamespaceAware(true);
		factory.setValidating(validating);

		if (useSchema == true) {
			factory.setAttribute(SCHEMA_LANGUAGE_ATTR_NAME, SCHEMA_LANGUAGE_ATTR_VALUE);

			if (schemaSource != null) {
				factory.setAttribute(SCHEMA_SOURCE_ATTR_NAME, schemaSource);
			}
		}

		factory.setIgnoringElementContentWhitespace(true);

		DocumentBuilder builder = factory.newDocumentBuilder();

		if (entityResolver != null) {
			builder.setEntityResolver(entityResolver);
		}

		if (errorHandler != null) {
			builder.setErrorHandler(errorHandler);
		} else {
			builder.setErrorHandler(new DefaultParserErrorHandler());
		}

		return builder;
	}

	/**
	 * Parse the specified XML document, and return a <code>TreeNode</code>
	 * that corresponds to the root node of the document tree.
	 * 
	 * @param uri
	 *            The location of the content to be parsed
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * 
	 * @exception Exception
	 */
	public TreeNode parse(String uri) {
		try {
			Document document = getDocumentBuilder().parse(uri);

			// Convert the resulting document to a graph of TreeNodes
			return (convert(null, document.getDocumentElement()));
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage(), e);
		}
	}

	/**
	 * Parse the specified XML document, and return a <code>TreeNode</code>
	 * that corresponds to the root node of the document tree.
	 * 
	 * @param uri
	 *            The location of the content to be parsed
	 * @throws ParserConfigurationException
	 * 
	 * @exception Exception
	 */
	public TreeNode parse(File f) {
		try {
			Document document = getDocumentBuilder().parse(f);

			// Convert the resulting document to a graph of TreeNodes
			return (convert(null, document.getDocumentElement()));
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage(), e);
		}
	}

	/**
	 * Parse the specified XML document, and return a <code>TreeNode</code>
	 * that corresponds to the root node of the document tree.
	 * 
	 * @param in
	 *            the input source of XML data
	 * @throws ParserConfigurationException
	 * 
	 * @exception Exception
	 */
	public TreeNode parse(InputSource is) {
		try {
			Document document = getDocumentBuilder().parse(is);
			// Convert the resulting document to a graph of TreeNodes
			return (convert(null, document.getDocumentElement()));
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage(), e);
		}
	}

	/**
	 * Create and return a TreeNode that corresponds to the specified Node,
	 * including processing all of the attributes and children nodes.
	 * 
	 * @param parent
	 *            The parent TreeNode (if any) for the new TreeNode
	 * @param node
	 *            The XML document Node to be converted
	 */
	public TreeNode convert(TreeNode parent, Node node) {

		// Construct a new TreeNode for this node
		TreeNode treeNode = new TreeNode(node.getNodeName(), parent);

		// Convert all attributes of this node
		NamedNodeMap attributes = node.getAttributes();
		if (attributes != null) {
			int n = attributes.getLength();
			for (int i = 0; i < n; i++) {
				Node attribute = attributes.item(i);
				treeNode.addAttribute(attribute.getNodeName(), attribute.getNodeValue());
			}
		}

		// Create and attach all children of this node
		NodeList children = node.getChildNodes();
		if (children != null) {
			int n = children.getLength();
			for (int i = 0; i < n; i++) {
				Node child = children.item(i);
				if (child instanceof Comment)
					continue;
				if (child instanceof Text) {
					String body = ((Text) child).getData();

					if (body != null) {
						body = body.trim();
						if (body.length() > 0)
							treeNode.setBody(body);
					}
				} else {
					convert(treeNode, child);
				}
			}
		}

		// Return the completed TreeNode graph
		return (treeNode);
	}

	public EntityResolver getEntityResolver() {
		return entityResolver;
	}

	public void setEntityResolver(EntityResolver entityResolver) {
		this.entityResolver = entityResolver;
	}

	public ErrorHandler getErrorHandler() {
		return errorHandler;
	}

	public void setErrorHandler(ErrorHandler errorHandler) {
		this.errorHandler = errorHandler;
	}

	public boolean isValidating() {
		return validating;
	}

	public void setValidating(boolean validating) {
		this.validating = validating;
	}

	public boolean isUseSchema() {
		return useSchema;
	}

	public void setUseSchema(boolean b) {
		useSchema = b;
	}

	public String getSchemaSource() {
		return schemaSource;
	}

	public void setSchemaSource(String string) {
		schemaSource = string;
	}

}